﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Jerry.DroidBuilder.Controls;

namespace Jerry.DroidBuilder.Extensions
{
    public class ColumnExtensionHelper
    {
        private readonly TreeListView _listView;

        private bool _loaded = false;
        private bool _resizing = false;

        private GridViewColumn _autoSizedColumn;
        private ScrollViewer _scrollViewer;

        /// <summary>
        /// Initializes a new instance of <see cref="ColumnExtensionHelper"/>.
        /// </summary>
        public ColumnExtensionHelper(TreeListView listView)
        {
            _listView = listView;

            _listView.Loaded -= OnListViewLoaded;
            _listView.Loaded += OnListViewLoaded;

            _listView.Unloaded -= OnListViewUnloaded;
            _listView.Unloaded += OnListViewUnloaded;
        }

        private void OnListViewLoaded(object sender, RoutedEventArgs e)
        {
            RegisterEvents(_listView);

            foreach (GridViewColumn column in _listView.Columns)
            {
                DependencyPropertyDescriptor.FromProperty(GridViewColumn.WidthProperty, typeof(GridViewColumn)).AddValueChanged(column, OnGridViewColumnWidthChanged);
            }

            DoResizeColumns();

            _loaded = true;
        }

        private void OnListViewUnloaded(object sender, RoutedEventArgs e)
        {
            if (!_loaded)
            {
                return;
            }

            UnregisterEvents(_listView);

            foreach (GridViewColumn column in _listView.Columns)
            {
                DependencyPropertyDescriptor.FromProperty(GridViewColumn.WidthProperty, typeof(GridViewColumn)).RemoveValueChanged(column, OnGridViewColumnWidthChanged);
            }

            _loaded = false;
        }

        private void OnGridViewColumnWidthChanged(object sender, EventArgs e)
        {
            if (!_loaded)
            {
                return;
            }

            GridViewColumn currentColumn = sender as GridViewColumn;

            if (currentColumn.Width.Equals(double.NaN))
            {
                _autoSizedColumn = currentColumn;
                return;
            }

            double minWidth = ColumnExtension.GetMinWidth(currentColumn);
            double maxWidth = GetMaxWidth(currentColumn);

            if (maxWidth < minWidth)
            {
                maxWidth = minWidth;
            }

            if (currentColumn.Width < minWidth)
            {
                currentColumn.Width = minWidth;
            }

            if (currentColumn.Width > maxWidth)
            {
                currentColumn.Width = maxWidth;
            }

            #region Resize Propotional Width

            if (ColumnExtension.IsProportionalColumn(currentColumn))
            {
                double resizeableRegionCount = 0;
                double otherColumnsWidth = 0;

                foreach (GridViewColumn column in _listView.Columns)
                {
                    if (ColumnExtension.IsProportionalColumn(column))
                    {
                        resizeableRegionCount += ColumnExtension.GetWidth(column).Value;
                    }
                    else
                    {
                        otherColumnsWidth += column.ActualWidth;
                    }
                }

                double resizeableColumnsWidth = GetActualWidth() - otherColumnsWidth;

                if (resizeableColumnsWidth <= 0)
                {
                    return;
                }

                // resize columns
                double resizeableRegionWidth = resizeableColumnsWidth / resizeableRegionCount;
                double currentRegionCount = currentColumn.ActualWidth / resizeableRegionWidth;

                double oldResizeableRegionCount = 0;
                double oldRegionCountWithoutCurrent = 0;
                List<GridViewColumn> remainingResizeableColumns = new List<GridViewColumn>();

                for (int i = _listView.Columns.Count - 1; i >= 0; i--)
                {
                    GridViewColumn column = _listView.Columns[i];

                    if (ColumnExtension.IsProportionalColumn(column))
                    {
                        remainingResizeableColumns.Add(column);
                        oldResizeableRegionCount += ColumnExtension.GetWidth(column).Value;
                    }

                    if (column == currentColumn)
                    {
                        break;
                    }

                    if (ColumnExtension.IsProportionalColumn(column))
                    {
                        oldRegionCountWithoutCurrent += ColumnExtension.GetWidth(column).Value;
                    }
                }

                double remainingRegionCount = oldResizeableRegionCount - currentRegionCount;

                ColumnExtension.SetWidth(remainingResizeableColumns[remainingResizeableColumns.Count - 1], new GridLength(currentRegionCount, GridUnitType.Star));

                for (int i = 0; i < remainingResizeableColumns.Count - 1; i++)
                {
                    GridViewColumn column = remainingResizeableColumns[i];
                    double oldCount = ColumnExtension.GetWidth(column).Value;
                    ColumnExtension.SetWidth(column, new GridLength(oldCount / oldRegionCountWithoutCurrent * remainingRegionCount, GridUnitType.Star));
                }
            }

            #endregion

            DoResizeColumns();
        }

        private bool HasProportionalColumn()
        {
            foreach (GridViewColumn column in _listView.Columns)
            {
                if (ColumnExtension.IsProportionalColumn(column))
                {
                    return true;
                }
            }

            return false;
        }

        private double GetMaxWidth(GridViewColumn gridViewColumn)
        {
            double actualWidth = GetActualWidth();

            if (double.IsInfinity(actualWidth) || actualWidth <= 0)
            {
                return double.PositiveInfinity;
            }

            bool isAfterCurrent = false;
            double minWidthTotal = 0;

            foreach (GridViewColumn column in _listView.Columns)
            {
                if (gridViewColumn == column)
                {
                    isAfterCurrent = true;
                }
                else
                {
                    if (isAfterCurrent)
                    {
                        minWidthTotal += ColumnExtension.GetMinWidth(column);
                    }
                    else
                    {
                        minWidthTotal += column.ActualWidth;
                    }
                }
            }

            return (actualWidth - minWidthTotal);
        }

        private double GetActualWidth()
        {
            double actualWidth = double.PositiveInfinity;

            if (null != _scrollViewer)
            {
                actualWidth = _scrollViewer.ViewportWidth;
            }

            if (double.IsInfinity(actualWidth))
            {
                actualWidth = _listView.ActualWidth;
            }

            return actualWidth;
        }

        /// <summary>
        /// handle autosized column
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GridColumnHeaderSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (null == _autoSizedColumn)
            {
                return;
            }

            GridViewColumnHeader gridViewColumnHeader = sender as GridViewColumnHeader;

            if (gridViewColumnHeader.Column == _autoSizedColumn)
            {
                if (gridViewColumnHeader.Width.Equals(double.NaN))
                {
                    // sync column with 
                    gridViewColumnHeader.Column.Width = gridViewColumnHeader.ActualWidth;
                    DoResizeColumns();
                }

                _autoSizedColumn = null;
            }
        }

        private void ScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (_loaded && 0 != e.ViewportWidthChange)
            {
                DoResizeColumns();
            }
        }

        private void RegisterEvents(DependencyObject start)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++)
            {
                Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual;

                if (childVisual is GridViewColumnHeader)
                {
                    GridViewColumnHeader columnHeader = childVisual as GridViewColumnHeader;

                    columnHeader.SizeChanged -= new SizeChangedEventHandler(GridColumnHeaderSizeChanged);
                    columnHeader.SizeChanged += new SizeChangedEventHandler(GridColumnHeaderSizeChanged);
                }
                else if (_scrollViewer == null && childVisual is ScrollViewer)
                {
                    _scrollViewer = childVisual as ScrollViewer;

                    _scrollViewer.ScrollChanged -= new ScrollChangedEventHandler(ScrollViewerScrollChanged);
                    _scrollViewer.ScrollChanged += new ScrollChangedEventHandler(ScrollViewerScrollChanged);

                    if (HasProportionalColumn())
                    {
                        _scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                    }
                }

                RegisterEvents(childVisual);
            }
        }

        private void UnregisterEvents(DependencyObject start)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++)
            {
                Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual;

                if (childVisual is GridViewColumnHeader)
                {
                    GridViewColumnHeader columnHeader = childVisual as GridViewColumnHeader;
                    columnHeader.SizeChanged -= new SizeChangedEventHandler(GridColumnHeaderSizeChanged);
                }
                else if (null == _scrollViewer && childVisual is ScrollViewer)
                {
                    _scrollViewer = childVisual as ScrollViewer;
                    _scrollViewer.ScrollChanged -= new ScrollChangedEventHandler(ScrollViewerScrollChanged);
                }

                UnregisterEvents(childVisual);
            }
        }

        private void DoResizeColumns()
        {
            if (_resizing)
            {
                return;
            }

            _resizing = true;

            try
            {
                ResizeColumns();
            }
            catch
            {
                throw;
            }
            finally
            {
                _resizing = false;
            }
        }

        protected virtual void ResizeColumns()
        {
            // listview width
            double actualWidth = GetActualWidth();

            if (double.IsInfinity(actualWidth) || actualWidth <= 0)
            {
                return;
            }

            double resizeableRegionCount = 0;
            double otherColumnsWidth = 0;

            // determine column sizes
            foreach (GridViewColumn gridViewColumn in _listView.Columns)
            {
                if (ColumnExtension.IsProportionalColumn(gridViewColumn))
                {
                    resizeableRegionCount += ColumnExtension.GetWidth(gridViewColumn).Value;
                }
                else
                {
                    otherColumnsWidth += gridViewColumn.ActualWidth;
                }
            }

            double resizeableColumnsWidth = actualWidth - otherColumnsWidth;

            if (resizeableColumnsWidth <= 0)
            {
                return;
            }

            // resize columns
            double resizeableRegionWidth = resizeableColumnsWidth / resizeableRegionCount;

            foreach (GridViewColumn gridViewColumn in _listView.Columns)
            {
                if (ColumnExtension.IsProportionalColumn(gridViewColumn))
                {
                    double minWidth = ColumnExtension.GetMinWidth(gridViewColumn);
                    double regionWidth = ColumnExtension.GetWidth(gridViewColumn).Value;

                    double currentWidth = regionWidth * resizeableRegionWidth;

                    if (currentWidth > resizeableColumnsWidth)
                    {
                        currentWidth = resizeableColumnsWidth;
                    }
                    else if (minWidth > currentWidth)
                    {
                        currentWidth = minWidth;
                    }

                    if (currentWidth < 0)
                    {
                        currentWidth = minWidth;
                    }

                    gridViewColumn.Width = currentWidth;

                    resizeableRegionCount -= regionWidth;
                    resizeableColumnsWidth -= currentWidth;
                    resizeableRegionWidth = resizeableColumnsWidth / resizeableRegionCount;
                }
            }
        }

        private GridViewColumnHeader FindColumnHeader(DependencyObject start, GridViewColumn gridViewColumn)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++)
            {
                Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual;

                if (childVisual is GridViewColumnHeader)
                {
                    GridViewColumnHeader gridViewHeader = childVisual as GridViewColumnHeader;

                    if (null != gridViewHeader && gridViewHeader.Column == gridViewColumn)
                    {
                        return gridViewHeader;
                    }
                }

                GridViewColumnHeader childGridViewHeader = FindColumnHeader(childVisual, gridViewColumn);  // recursive

                if (null != childGridViewHeader)
                {
                    return childGridViewHeader;
                }
            }
            return null;
        }
    }
}
